package se.webbzon.boltzmann.minigame.chemistry;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import se.webbzon.boltzmann.minigame.sandbox.DefaultSandBoxContainer;
import se.webbzon.boltzmann.minigame.sandbox.DefaultSandBoxItem;
import se.webbzon.boltzmann.minigame.sandbox.SandBox;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxContainer;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxIcon;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxItem;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxListener;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxObject;
import se.webbzon.boltzmann.resource.Resources;
import se.webbzon.boltzmann.sound.SoundUtils;
import se.webbzon.oschi01.resourceset.ResourceSet;
import se.webbzon.oschi01.textureset.Texture3D;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldcamera.WorldCameraPane;
import se.webbzon.oschi01.worldobject.WorldObject;

public class Gasoline extends DefaultSandBoxItem {
	
	// The 2-dimensional depth of this gasoline burner
	private static final int depth2d = 50;
	
	// The width and height of this gasoline burner
	private static final int width = 96, height = 96;
	
	// The texture set of this gasoline burner
	private final ResourceSet<Texture3D> textureSet;
	
	// The reaction containers of this gasoline burner
	private final DefaultSandBoxContainer left, right, upper;
	
	// The sand box listener of this gasoline burner
	private final SandBoxListener listener;
	
	// The sand box to which this gasoline burner is attached
	private SandBox box;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public Gasoline() {
		textureSet = Resources.getTextureSet("chemistry_gasoline");
		left = new DefaultSandBoxContainer();
		right = new DefaultSandBoxContainer();
		upper = new DefaultSandBoxContainer();
		listener = new SandBoxListenerImpl();
		setInstance(new Instance());
		setSize(width,height);
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Processes the provided reagents. If null is returned then no
	 * reaction occurred. **/
	public static final Product process(SandBoxMolecule[] reagents) {
		ArrayList<CarbonMolecule> carbon = new ArrayList<CarbonMolecule>();
		ArrayList<SugarMolecule> sugar = new ArrayList<SugarMolecule>();
		ArrayList<OxygenMolecule> oxygen = new ArrayList<OxygenMolecule>();
		ArrayList<HydrogenMolecule> hydrogen = new ArrayList<HydrogenMolecule>();
		for (SandBoxMolecule reagent : reagents) {
			if (reagent instanceof SugarMolecule) 
				sugar.add((SugarMolecule) reagent);
			else if (reagent instanceof OxygenMolecule) 
				oxygen.add((OxygenMolecule) reagent);
			else if (reagent instanceof HydrogenMolecule)
				hydrogen.add((HydrogenMolecule) reagent);
			else  if (reagent instanceof CarbonMolecule)
				carbon.add((CarbonMolecule) reagent);
		}
		if (sugar.size() == 0 && oxygen.size() == 0 && hydrogen.size() == 0)
			return null;
		
		int outWater = 0;
		int outCarbon = 0;
		int outCarbonDioxide = 0;
		Collection<SandBoxMolecule> consumed = new ArrayList<SandBoxMolecule>();
		
		// Consume sugar
		outWater += 6 * sugar.size();
		outCarbon += 6 * sugar.size();
		consumed.addAll(sugar);
		
		// Consume carbon and oxygen
		outCarbonDioxide += Math.min(oxygen.size(), carbon.size());
		Iterator<OxygenMolecule> oin = oxygen.iterator();
		Iterator<CarbonMolecule> cin = carbon.iterator();
		for (int i = 0; i < outCarbonDioxide; i++) {
			consumed.add(oin.next());
			consumed.add(cin.next());
			oin.remove();
			cin.remove();
		}
		
		// Consume oxygen and hydrogen
		int n = Math.min(hydrogen.size() / 2, oxygen.size());
		outWater += 2 * n;
		oin = oxygen.iterator();
		Iterator<HydrogenMolecule> hin = hydrogen.iterator();
		for (int i = 0; i < n; i++) {
			consumed.add(oin.next());
			oin.remove();
			consumed.add(hin.next());
			hin.remove();
			consumed.add(hin.next());
			hin.remove();
		}
		
		if (consumed.isEmpty())
			return null;
		else {
			Product p = new Product();
			p.consumed = consumed.toArray(new SandBoxMolecule[0]);
			p.upper = new SandBoxMolecule[outCarbonDioxide + outWater];
			for (int i = 0; i < outWater; i++)
				p.upper[i] = new WaterMolecule();
			for (int i = outWater; i < outWater + outCarbonDioxide; i++)
				p.upper[i] = new CarbonDioxideMolecule();
			p.lower = new SandBoxMolecule[outCarbon];
			for (int i = 0; i < outCarbon; i++)
				p.lower[i] = new CarbonMolecule();
			return p;
		}
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** A product of this gasoline burner. **/
	public static final class Product {
		SandBoxMolecule[] upper = null;
		SandBoxMolecule[] lower = null;
		SandBoxMolecule[] consumed = null;
	}
	
	/*============================================================
	Public Classes
	============================================================*/
	
	private final class Instance extends SandBoxObject {

		public Instance() {
			super(WorldObject.onPostDraw, depth2d);
		}

		@Override public void onAttach(SandBox box) {
			box.addItem(left);
			box.addItem(right);
			box.addItem(upper);
			Gasoline.this.box = box;
			box.addListener(listener);
		}

		@Override public void onDetach(SandBox box) {
			box.removeItem(left);
			box.removeItem(right);
			box.removeItem(upper);
			box.removeListener(listener);
			Gasoline.this.box = null;
		}

		@Override public void setPosition(double x, double y) {
			super.setPosition(x, y);
			left.setPosition(x - 28, y - 80);
			right.setPosition(x + 28, y - 80);
			upper.setPosition(x, y - 132);
		}

		@Override public void onPostDraw(WorldArea world, WorldCameraPane hud) {
			int x = (int) getX() - width/2, y = (int) getY() - height/2;
			hud.drawTexture(textureSet.get(0), x, y, width, height);
		}

		@Override public SandBoxIcon createIcon() {
			return null;
		}
		
	}
	
	private final class SandBoxListenerImpl implements SandBoxListener {

		@Override public void onInvoke(SandBox box, double x, double y) {};

		@Override public void onPick(SandBox box, SandBoxItem item, SandBoxContainer container) {
			if (left == container || right == container || upper == container) {
				process();
			}
		}

		@Override public void onDrop(SandBox box, SandBoxItem item, SandBoxContainer container) {
			if (left == container || right == container) {
				process();
			}
		}
		
		/** Triggers the gasoline burner process. **/
		private void process() {
			if (upper.isEmpty()) {
				Collection<SandBoxItem> leftItems = left.getContent();
				Collection<SandBoxItem> rightItems = right.getContent();
				ArrayList<SandBoxMolecule> molecules = new ArrayList<SandBoxMolecule>();
				for (SandBoxItem item : leftItems) {
					if (item instanceof SandBoxMolecule)
						molecules.add((SandBoxMolecule) item);
				}
				for (SandBoxItem item : rightItems) {
					if (item instanceof SandBoxMolecule)
						molecules.add((SandBoxMolecule) item);
				}
				Product p = Gasoline.process(molecules.toArray(new SandBoxMolecule[0]));
				if (p != null) {
					if (left.isEmpty()) {
						for (SandBoxMolecule m : p.lower)
							left.putItem(box, m);
						for (SandBoxMolecule m : p.upper)
							upper.putItem(box, m);
						for (SandBoxMolecule m : p.consumed)
							right.removeContent(m);
						SoundUtils.playSound("burner");
					} else if (right.isEmpty()) {
						for (SandBoxMolecule m : p.lower)
							right.putItem(box, m);
						for (SandBoxMolecule m : p.upper)
							upper.putItem(box, m);
						for (SandBoxMolecule m : p.consumed)
							left.removeContent(m);
						SoundUtils.playSound("burner");
					} else if (p.lower.length == 0) {
						for (SandBoxMolecule m : p.upper)
							upper.putItem(box, m);
						for (SandBoxMolecule m : p.consumed) {
							left.removeContent(m);
							right.removeContent(m);
						}
						SoundUtils.playSound("burner");
					}
				}
			}
		}
		
	}

}
